#include <fstream>
#include <sstream>
#include <istream>
#include <algorithm>
#include <stdexcept>
#include <vector>
#include <deque>
#include <string>
#include <iostream>
#include <iomanip>

//#include <unistd.h>
//#include <sys/types.h>
//#include <sys/stat.h>


#define DEBUG_LEVEL 6
#include <debug_func.h>

#include <portable_timer.h>
#include <progress_indicator.h>
#include <stlhelper.h>
#include <ultoa.h>
#include <print_error.h>

#include <md4.h>
#include <digest.h>
#include <open_fstream.h>
#include <get_filename.h>
#include <seq_pairs.h>
#include "get_arg.h"

using std::istringstream;
using std::vector;
using std::deque;
using std::ofstream;
using std::ifstream;
using std::istream;
using std::string;
using std::runtime_error;
using std::cerr;
using std::cin;
using std::cout;


//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

//		Main

//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

int main(int argc, char* argv[])
{
	t_program_args args;

	try
	{

	debug_func_cerr(5);

	// don't use C streams
	std::ios::sync_with_stdio(false);


	// get command line arguments
	if (!process_command_line_options(argc, argv, args))
		return 1;


	ifstream is;
	string file_path = args.file_input;
	string file_name = get_filename(file_path);
	istream::pos_type file_size = open_fstream(is, file_path);
	args.VERBOSE() << "\tReading sequence pair data from [" + file_name + "]\n";

	ofstream f_summary;
	if (!args.file_summary.empty())
		f_summary.open(args.file_summary.c_str());

	//
	//	Scanning all ids
	//
	unsigned cnt_line = 0;
	deque<t_digest> id_digests;
	{
		unsigned dots_size = 50000;
		t_progress_indicator dots(args.VERBOSE(), dots_size);
		dots.use_timer();

		debug_func_cerr(5);
		unsigned cnt_line = 0;

		//
		// handle any header
		//
		if (seq_pairs::skip_header(is))
			cnt_line += 2;

		try 
		{
			// get line
			string line;
			while (getline(is, line))
			{
				++cnt_line;
				++dots;

				// split out IDs unless comment
				string ids;
				if (!seq_pairs::parse_normalized_ids_sub_string(line, ids, args.comment_letters))
					continue;

				// calculate and save hash
				t_digest md4;
				md4_hash(ids, md4);
				id_digests.push_back(md4);
			}
		}
		catch(std::runtime_error& e)
		{
			throw std::runtime_error(string(e.what()) + 
									 " on line #" + ultoa(cnt_line) + " of " + file_name);
		}
		if (!cnt_line)
			throw std::runtime_error("No sequence pair data supplied in [" + file_name + "].");
	}
	args.VERBOSE() << "\t" << id_digests.size() << " results read.\n";
	f_summary << std::setw(10) << id_digests.size() << "\tresults read.\n";
	args.VERBOSE() << "\tSorting ID list...\n";
	sort(id_digests.begin(), id_digests.end());
	args.VERBOSE() << "\tRemoving duplicates...\n";
	remove_duplicates(id_digests);
	args.VERBOSE() << "\t" << id_digests.size() << " unique results.\n";
	f_summary << std::setw(10) << id_digests.size() << "\tunique results\n";


	vector<unsigned> counts(id_digests.size());
	//
	//	Pass through none duplicates to stdout
	//
	{
		args.VERBOSE() << "\tPrinting out non-duplicates...\n";
		t_progress_indicator dots(args.VERBOSE(),  500000);
		dots.use_timer();

		// start from scratch
		is.clear();
		is.seekg(0, ifstream::beg);

		//
		// handle any header
		//
		if (seq_pairs::skip_header(is))
			cnt_line += 2;

		// get line
		string line;
		while (getline(is, line))
		{
			++dots;

			// split out IDs unless comments, when line is passed through
			// does not need to be wrapped in exception handler because any
			// funny data would have been caught in the first pass
			string ids;
			if (!seq_pairs::parse_normalized_ids_sub_string(line, ids, args.comment_letters))
			{
				cout << line << "\n";
				continue;
			}

			// find crc
			t_digest md4;
			md4_hash(ids, md4);
			unsigned pos = find_pos_in_sorted(id_digests, md4);
			assert (pos < id_digests.size());
			++counts[pos];
			if (counts[pos] == 1)
				cout << line << "\n";
		}
	}

	std::map<unsigned,  unsigned> histogram_counts;
	for (unsigned i = 0; i < counts.size(); ++i)
		++histogram_counts[counts[i]];

	std::map<unsigned, unsigned>::iterator i = histogram_counts.begin();
	std::map<unsigned, unsigned>::iterator end = histogram_counts.end();
	while (i != end)
	{
		args.VERBOSE(2) << "\t" << std::setw(10) << i->second << " items had " << i->first << " duplicates\n";
		f_summary << std::setw(10) << i->second << "\titems had\t" << i->first << "\tduplicates\n";
		++i;
	}

	args.VERBOSE() << "\tAll finished.\n";

	}
	catch (std::exception& e)
	{
		std_print_error(args.CERR(), e.what());
		return 1;
	}

	return 0;
}

